﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Data.Linq;
using System.Text;
using System.Web.Mvc;
using Microsoft.Practices.Unity;
using Vit.Common;
using Vit.Common.Models;
using Vit.Common.Extensions;
using Vit.Web;
using Vit.Web.Models;
using Vit.Mvc.Providers;
using Vit.Mvc.Validation;
using Vit.Mvc.Extensions;
using Vit.Mvc.DataAnnotations;

namespace Vit.Mvc.Controllers
{
    public abstract class EntityControllerBase<TDB, TEntity> : UnityControllerBase
        where TDB : DataContext, new()
        where TEntity : class, new()
    {
        protected TDB db = new TDB();

        [HttpGet]
        public virtual ActionResult Index(int id = 1)
        {
            return QueryView("Index", null, IndexPageSize, id);
        }

        [HttpPost]
        [ValidateInput(false)]
        public virtual ActionResult Index(FormCollection form, int id = 1)
        {
            return QueryView("Index", form, IndexPageSize, id);
        }

        [HttpGet]
        public virtual ActionResult Create()
        {
            TEntity obj = new TEntity();
            InitializeEntity(obj);
            return DetermineActionResult("Edit", null, obj);
        }

        [HttpPost]
        [ValidateInput(false)]
        public virtual ActionResult Create(FormCollection form)
        {
            TEntity entity = new TEntity();
            UpdateEntityFromForm(entity, form);
            if (this.ModelState.IsValid)
            {
                ValidationStateDictionary validationState = Validate(entity, DataOperationType.Add);

                if (validationState.IsValid)
                {
                    var tbl = db.GetTable<TEntity>();
                    tbl.InsertOnSubmit(entity);
                    db.SubmitChanges();
                    return RedirectToAction("Index");
                }
                else
                {
                    ValidationException ex = new ValidationException() { ValidationState = validationState };
                    this.ModelState.AddModelErrors(ex.ValidationState);
                    return DetermineActionResult("Edit", null, entity);
                }
            }
            else
            {
                return DetermineActionResult("Edit", null, entity);
            }
        }

        [HttpGet]
        public virtual ActionResult Detail(string id)
        {
            var tbl = db.GetTable<TEntity>();
            TEntity entity = tbl.FirstOrDefault(GetEnityKeyPredict(id));
            return DetermineActionResult(null, null, entity);
        }

        [HttpGet]
        public virtual ActionResult Edit(string id)
        {
            var tbl = db.GetTable<TEntity>();
            TEntity entity = tbl.FirstOrDefault(GetEnityKeyPredict(id));
            return DetermineActionResult(null, null, entity);
        }

        [HttpPost]
        [ValidateInput(false)]
        public virtual ActionResult Edit(FormCollection form)
        {
            TEntity entity = new TEntity();
            UpdateEntityFromForm(entity, form);
            if (this.ModelState.IsValid)
            {
                ValidationStateDictionary validationState = Validate(entity, DataOperationType.Add);

                if (validationState.IsValid)
                {
                    db.GetTable<TEntity>().Attach(entity, true);
                    db.SubmitChanges();
                    return DetermineActionResult(null, null, entity);
                }
                else
                {
                    ValidationException ex = new ValidationException() { ValidationState = validationState };
                    this.ModelState.AddModelErrors(ex.ValidationState);
                    return DetermineActionResult("Edit", null, entity);
                }
            }
            else
            {
                return View(entity);
            }
        }

        [HttpGet]
        public virtual ActionResult Delete(string id)
        {
            var tbl = db.GetTable<TEntity>();
            TEntity entity = tbl.FirstOrDefault(GetEnityKeyPredict(id));
            if (entity != null)
            {
                tbl.DeleteOnSubmit(entity);
                db.SubmitChanges();
            }
            return RedirectToAction("Index");
        }

        [HttpPost]
        [ValidateInput(false)]
        public virtual ActionResult Delete(FormCollection form)
        {
            var tbl = db.GetTable<TEntity>();
            foreach (string fid in form.AllKeys.Where(o => o.StartsWith("cb_")))
            {
                string val = form[fid];

                if (string.IsNullOrEmpty(val) || val.ConvertTo<bool>(false) == false)
                    continue;

                val = fid.Substring(3);
                TEntity entity = tbl.FirstOrDefault(GetEnityKeyPredict(val));
                if (entity != null)
                    tbl.DeleteOnSubmit(entity);
            }
            db.SubmitChanges();
            return RedirectToAction("Index");
        }

        [HttpGet]
        public virtual ActionResult Dialog(int id = 1)
        {
            return QueryView("Dialog", null, DialogPageSize, id);
        }

        [HttpPost]
        [ValidateInput(false)]
        public virtual ActionResult Dialog(FormCollection form, int id = 1)
        {
            return QueryView("Dialog", form, DialogPageSize, id);
        }

        protected virtual ActionResult QueryView(string viewName, FormCollection form, int pageSize, int id = 1)
        {
            db.ObjectTrackingEnabled = false;
            var query = db.GetTable<TEntity>();

            var filteredQuery = FilterQuery(form, query);

            if (id > 0 && IndexPageSize > 0)
            {
                PageOfList<TEntity> result = new PageOfList<TEntity>(query, id, pageSize);
                return DetermineActionResult(viewName, null, result);
            }
            return DetermineActionResult(viewName, null, filteredQuery);
        }

        protected virtual void UpdateEntityFromForm(TEntity entity, FormCollection form)
        {
            this.TryUpdateModel<TEntity>(entity);
        }

        protected virtual IQueryable<TEntity> FilterQuery(FormCollection form, IQueryable<TEntity> query)
        {
            return query;
        }

        protected virtual void InitializeEntity(TEntity entity)
        {
        }

        protected abstract Expression<Func<TEntity, bool>> GetEnityKeyPredict(string id);

        [NonAction]
        protected virtual ActionResult DetermineActionResult(string viewName, string masterName, object model)
        {
            if (string.IsNullOrEmpty(viewName))
                viewName = this.RouteData.GetRequiredString("action");

            if (this.ControllerContext.IsChildAction)
                return PartialView(viewName, model);
            else
            {
                if (string.IsNullOrEmpty(masterName))
                    return View(viewName, model);
                else
                    return View(viewName, masterName, model);
            }
        }

        protected virtual ValidationStateDictionary Validate(TEntity obj, DataOperationType dataOperation)
        {
            ValidationStateDictionary validationState = new ValidationStateDictionary();
            if (WebUnity.Current.UnityContainer.IsRegistered<IValidator<TEntity>>())
            {
                IValidator<TEntity> validator = WebUnity.Current.UnityContainer.Resolve<IValidator<TEntity>>();
                ValidationState state = validator.Validate(obj, dataOperation);
                validationState.Add(typeof(TEntity), state);
            }
            return validationState;
        }

        protected virtual int IndexPageSize { get { return 20; } }
        protected virtual int DialogPageSize { get { return 20; } }
    }
}
